
import { GameManager, regGameManager } from "../GameManager";
import { LoggerManager } from "./LoggerManager";

export class ConfigTable<T>{
    private _data_map = new Map()
    private _data_array: T[]
    constructor(dataTable) {
        this._data_array = dataTable
        dataTable.forEach((data, index) => {
            const keys = Object.keys(data)
            /**第一列作为id索引 */
            const id = data['id'] || data[keys[0]]
            this._data_map.set(id, data)
        });
    }
    /**获取第n行数据 */
    public getRow(rowIndex: number): T {
        return this._data_array[rowIndex]
    }
    /**根据索引获取一行数据 */
    public getDataById(id): T {
        return this._data_map.get(id)
    }
    public get array(): T[] {
        return this._data_array
    }
    public get length(): number {
        return this._data_array.length
    }
    /**获取最后一行数据 */
    get lastRow() {
        return this._data_array[this._data_array.length - 1]
    }
}

/**配置表管理器 */
@regGameManager()
export class ConfigManager extends GameManager {

    /**初始化配置表管理器 */
    public async initialize(): Promise<void> {
        return new Promise(
            async (resolve) => {
                /**从数据源Config.dataMap中解析配置表 */
                const dataMap = (regConfig as any).dataMap
                for (const key in dataMap) {
                    //@ts-ignore
                    let json = dataMap[key]
                    this.setConfig(key, json);
                }

                /**从resources/config目录中加载配置表 */
                await this.loadConfigDir()


                resolve()
            }
        )

    }

    /**加载 config目录 中的配置表*/
    loadConfigDir(): Promise<void> {
        return new Promise(
            (resolve) => {
                cc.assetManager.getBundle('resources').loadDir(
                    "config",
                    async (e, assets) => {
                        if (e) {
                            LoggerManager.instance().log("load config error:", e);
                            resolve()
                            return
                        }
                        assets.forEach((item) => {
                            let asset = item as cc.JsonAsset;
                            this.setConfig(asset.name, asset.json);
                        });
                        resolve()
                    }
                );
            }
        )
    }


    public setConfig<T>(configFieldName: string, data: any) {
        LoggerManager.instance().log("setConfig:", configFieldName, data)
        if (data instanceof Array) {
            this[configFieldName] = new ConfigTable<T>(data)
        } else {
            this[configFieldName] = data;
        }
    }

    /**
     * 获取对象映射型配置表
     * @param config 
     * @returns 
     */
    public mapCfg<T>(config: ((new () => T))): T {
        return this.getCfgTableOrMapCfg(config)
    }


    /**
     * 获取列表型配置表中的一行
     * @param config 
     * @returns 
     */
    public cfgById<T>(config: ((new () => T)), id: number | string): T {
        return (this.getCfgTableOrMapCfg(config) as ConfigTable<T>).getDataById(id)
    }

    /**
     * 获取列表型配置表中的所有行
     * @param config 
     * @returns 
     */
    public cfgArr<T>(config: ((new () => T))): T[] {
        return (this.cfgTable(config) as ConfigTable<T>).array
    }
    /**
     * 获取列表型配置表
     * @param config 
     * @returns 
     */
    public cfgTable<T>(config: ((new () => T))): ConfigTable<T> {
        return this.getCfgTableOrMapCfg(config)
    }

    /**返回一个列表型配置表或映射表型配置表 */
    private getCfgTableOrMapCfg(config) {
        if (!(config as any).__configName) {
            throw new Error("该类型不是一个正确的表格，请检查是否使用了Config装饰器");
        }
        return this[(config as any).__configName]
    }

}

/**
 * 表格配置的类型装饰器
 * @param configName 配置表的名称(后缀加上Cfg)
 * @param isMap 是否是映射表
 * @param dataSource 配置表的数据源，不传就从MainCfg中获取
 * @returns 
 */
export function regConfig(configName: string, isMap = false, dataSource: any = null): ClassDecorator {
    //@ts-ignore
    if (!regConfig.clzMap) {
        //@ts-ignore
        regConfig.clzMap = {}
    }

    //手动提供的配置表数据源
    if (dataSource) {
        //@ts-ignore
        if (!regConfig.dataMap) {
            //@ts-ignore
            regConfig.dataMap = {}
        }
        //@ts-ignore
        regConfig.dataMap[configName] = dataSource
    }


    return function (target) {
        //@ts-ignore
        regConfig.clzMap[configName] = target
        Object.defineProperty(
            target,
            '__configName',
            {
                value: configName,
                writable: false,
                enumerable: false
            }
        )

        Object.defineProperty(
            target,
            '__isMap',
            {
                value: isMap,
                writable: false,
                enumerable: false
            }
        )
    }
}
